/** biome-ignore-all lint/suspicious/noExplicitAny: This use requires any */
"use client";

import * as React from "react";

import { type Emoji } from "@emoji-mart/data";

import {
  type EmojiCategoryList,
  type EmojiIconList,
  type GridRow,
  EmojiSettings,
} from "@platejs/emoji";
import {
  type EmojiDropdownMenuOptions,
  type UseEmojiPickerType,
  useEmojiDropdownMenuState,
} from "@platejs/emoji/react";
import * as Popover from "@radix-ui/react-popover";
import {
  AppleIcon,
  ClockIcon,
  CompassIcon,
  FlagIcon,
  LeafIcon,
  LightbulbIcon,
  MusicIcon,
  SearchIcon,
  SmileIcon,
  StarIcon,
  XIcon,
} from "lucide-react";

import { Button } from "@/components/plate/ui/button";
import { ToolbarButton } from "@/components/plate/ui/toolbar";
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from "@/components/plate/ui/tooltip";
import { cn } from "@/lib/utils";

export function EmojiToolbarButton({
  options,
  ...props
}: {
  options?: EmojiDropdownMenuOptions;
} & React.ComponentPropsWithoutRef&lt;typeof ToolbarButton&gt;) {
  const { emojiPickerState, isOpen, setIsOpen } =
    useEmojiDropdownMenuState(options);

  return (
    &lt;EmojiPopover
      control={
        &lt;ToolbarButton pressed={isOpen} tooltip="Emoji" isDropdown {...props}&gt;
          &lt;SmileIcon /&gt;
        &lt;/ToolbarButton&gt;
      }
      isOpen={isOpen}
      setIsOpen={setIsOpen}
    &gt;
      &lt;EmojiPicker
        {...emojiPickerState}
        isOpen={isOpen}
        setIsOpen={setIsOpen}
        settings={options?.settings}
      /&gt;
    &lt;/EmojiPopover&gt;
  );
}

export function EmojiPopover({
  children,
  control,
  isOpen,
  setIsOpen,
}: {
  children: React.ReactNode;
  control: React.ReactNode;
  isOpen: boolean;
  setIsOpen: (open: boolean) =&gt; void;
}) {
  return (
    &lt;Popover.Root open={isOpen} onOpenChange={setIsOpen}&gt;
      &lt;Popover.Trigger asChild&gt;{control}&lt;/Popover.Trigger&gt;

      &lt;Popover.Portal&gt;
        &lt;Popover.Content className="z-100"&gt;{children}&lt;/Popover.Content&gt;
      &lt;/Popover.Portal&gt;
    &lt;/Popover.Root&gt;
  );
}

export function EmojiPicker({
  clearSearch,
  emoji,
  emojiLibrary,
  focusedCategory,
  hasFound,
  i18n,
  icons = {
    categories: emojiCategoryIcons,
    search: emojiSearchIcons,
  },
  isSearching,
  refs,
  searchResult,
  searchValue,
  setSearch,
  settings = EmojiSettings,
  visibleCategories,
  handleCategoryClick,
  onMouseOver,
  onSelectEmoji,
}: Omit&lt;UseEmojiPickerType, "icons"&gt; & {
  icons?: EmojiIconList&lt;React.ReactElement&gt;;
}) {
  return (
    <div>
      &lt;EmojiPickerNavigation
        onClick={handleCategoryClick}
        emojiLibrary={emojiLibrary}
        focusedCategory={focusedCategory}
        i18n={i18n}
        icons={icons}
      /&gt;
      &lt;EmojiPickerSearchBar
        i18n={i18n}
        searchValue={searchValue}
        setSearch={setSearch}
      &gt;
        &lt;EmojiPickerSearchAndClear
          clearSearch={clearSearch}
          i18n={i18n}
          searchValue={searchValue}
        /&gt;
      &lt;/EmojiPickerSearchBar&gt;
      &lt;EmojiPickerContent
        onMouseOver={onMouseOver}
        onSelectEmoji={onSelectEmoji}
        emojiLibrary={emojiLibrary}
        i18n={i18n}
        isSearching={isSearching}
        refs={refs}
        searchResult={searchResult}
        settings={settings}
        visibleCategories={visibleCategories}
      /&gt;
      &lt;EmojiPickerPreview
        emoji={emoji}
        hasFound={hasFound}
        i18n={i18n}
        isSearching={isSearching}
      /&gt;
    </div>
  );
}

const EmojiButton = React.memo(function EmojiButton({
  emoji,
  index,
  onMouseOver,
  onSelect,
}: {
  emoji: Emoji;
  index: number;
  onMouseOver: (emoji?: Emoji) =&gt; void;
  onSelect: (emoji: Emoji) =&gt; void;
}) {
  return (
    &lt;button
      className="group relative flex size-9 cursor-pointer items-center justify-center border-none bg-transparent text-2xl leading-none"
      onClick={() =&gt; onSelect(emoji)}
      onMouseEnter={() =&gt; onMouseOver(emoji)}
      onMouseLeave={() =&gt; onMouseOver()}
      aria-label={emoji?.skins[0]?.native}
      data-index={index}
      tabIndex={-1}
      type="button"
    &gt;
      <div />
      <span style="{{" data-emoji-set="native">
        {emoji?.skins[0]?.native}
      </span>
    &lt;/button&gt;
  );
});

const RowOfButtons = React.memo(function RowOfButtons({
  emojiLibrary,
  row,
  onMouseOver,
  onSelectEmoji,
}: {
  row: GridRow;
} & Pick&lt;
  UseEmojiPickerType,
  "emojiLibrary" | "onMouseOver" | "onSelectEmoji"
&gt;) {
  return (
    <div data-index="{row.id}">
      {row.elements.map((emojiId, index) =&gt; (
        &lt;EmojiButton
          key={emojiId}
          onMouseOver={onMouseOver}
          onSelect={onSelectEmoji}
          emoji={emojiLibrary.getEmoji(emojiId)}
          index={index}
        /&gt;
      ))}
    </div>
  );
});

function EmojiPickerContent({
  emojiLibrary,
  i18n,
  isSearching = false,
  refs,
  searchResult,
  settings = EmojiSettings,
  visibleCategories,
  onMouseOver,
  onSelectEmoji,
}: Pick&lt;
  UseEmojiPickerType,
  | "emojiLibrary"
  | "i18n"
  | "isSearching"
  | "onMouseOver"
  | "onSelectEmoji"
  | "refs"
  | "searchResult"
  | "settings"
  | "visibleCategories"
&gt;) {
  const getRowWidth = settings.perLine.value * settings.buttonSize.value;

  const isCategoryVisible = React.useCallback(
    (categoryId: any) =&gt; {
      return visibleCategories.has(categoryId)
        ? visibleCategories.get(categoryId)
        : false;
    },
    [visibleCategories],
  );

  const EmojiList = React.useCallback(() =&gt; {
    return emojiLibrary
      .getGrid()
      .sections()
      .map(({ id: categoryId }) =&gt; {
        const section = emojiLibrary.getGrid().section(categoryId);
        const { buttonSize } = settings;

        return (
          <div style="{{" data-id="{categoryId}">
            <div>
              {i18n.categories[categoryId]}
            </div>
            <div style="{{">
              {isCategoryVisible(categoryId) &&
                section
                  .getRows()
                  .map((row: GridRow) =&gt; (
                    &lt;RowOfButtons
                      key={row.id}
                      onMouseOver={onMouseOver}
                      onSelectEmoji={onSelectEmoji}
                      emojiLibrary={emojiLibrary}
                      row={row}
                    /&gt;
                  ))}
            </div>
          </div>
        );
      });
  }, [
    emojiLibrary,
    getRowWidth,
    i18n.categories,
    isCategoryVisible,
    onSelectEmoji,
    onMouseOver,
    settings,
  ]);

  const SearchList = React.useCallback(() =&gt; {
    return (
      <div style="{{" data-id="search">
        <div>
          {i18n.searchResult}
        </div>
        <div>
          {searchResult.map((emoji: Emoji, index: number) =&gt; (
            &lt;EmojiButton
              key={emoji.id}
              onMouseOver={onMouseOver}
              onSelect={onSelectEmoji}
              emoji={emojiLibrary.getEmoji(emoji.id)}
              index={index}
            /&gt;
          ))}
        </div>
      </div>
    );
  }, [
    emojiLibrary,
    getRowWidth,
    i18n.searchResult,
    searchResult,
    onSelectEmoji,
    onMouseOver,
  ]);

  return (
    <div data-id="scroll">
      <div>
        {isSearching ? SearchList() : EmojiList()}
      </div>
    </div>
  );
}

function EmojiPickerSearchBar({
  children,
  i18n,
  searchValue,
  setSearch,
}: {
  children: React.ReactNode;
} & Pick&lt;UseEmojiPickerType, "i18n" | "searchValue" | "setSearch"&gt;) {
  return (
    <div>
      <div>
        <input> setSearch(event.target.value)}
          placeholder={i18n.search}
          aria-label="Search"
          autoComplete="off"
          type="text"
          autoFocus
        /&gt;
        {children}
      </div>
    </div>
  );
}

function EmojiPickerSearchAndClear({
  clearSearch,
  i18n,
  searchValue,
}: Pick&lt;UseEmojiPickerType, "clearSearch" | "i18n" | "searchValue"&gt;) {
  return (
    <div>
      <div>
        {emojiSearchIcons.loupe}
      </div>
      {searchValue && (
        &lt;Button
          size="icon"
          variant="ghost"
          className={cn(
            "absolute right-0.5 top-1/2 flex size-8 -translate-y-1/2 cursor-pointer items-center justify-center rounded-full border-none bg-transparent text-popover-foreground hover:bg-transparent",
          )}
          onClick={clearSearch}
          title={i18n.clear}
          aria-label="Clear"
          type="button"
        &gt;
          {emojiSearchIcons.delete}
        &lt;/Button&gt;
      )}
    </div>
  );
}

function EmojiPreview({ emoji }: Pick&lt;UseEmojiPickerType, "emoji"&gt;) {
  return (
    <div>
      <div>
        {emoji?.skins[0]?.native}
      </div>
      <div>
        <div>{emoji?.name}</div>
        <div>{`:${emoji?.id}:`}</div>
      </div>
    </div>
  );
}

function NoEmoji({ i18n }: Pick&lt;UseEmojiPickerType, "i18n"&gt;) {
  return (
    <div>
      <div>😢</div>
      <div>
        <div>
          {i18n.searchNoResultsTitle}
        </div>
        <div>{i18n.searchNoResultsSubtitle}</div>
      </div>
    </div>
  );
}

function PickAnEmoji({ i18n }: Pick&lt;UseEmojiPickerType, "i18n"&gt;) {
  return (
    <div>
      <div>☝️</div>
      <div>
        <div>{i18n.pick}</div>
      </div>
    </div>
  );
}

function EmojiPickerPreview({
  emoji,
  hasFound = true,
  i18n,
  isSearching = false,
  ...props
}: Pick&lt;UseEmojiPickerType, "emoji" | "hasFound" | "i18n" | "isSearching"&gt;) {
  const showPickEmoji = !emoji && (!isSearching || hasFound);
  const showNoEmoji = isSearching && !hasFound;
  const showPreview = emoji && !showNoEmoji && !showNoEmoji;

  return (
    &lt;&gt;
      {showPreview && &lt;EmojiPreview emoji={emoji} {...props} /&gt;}
      {showPickEmoji && &lt;PickAnEmoji i18n={i18n} {...props} /&gt;}
      {showNoEmoji && &lt;NoEmoji i18n={i18n} {...props} /&gt;}
    &lt;/&gt;
  );
}

function EmojiPickerNavigation({
  emojiLibrary,
  focusedCategory,
  i18n,
  icons,
  onClick,
}: {
  onClick: (id: EmojiCategoryList) =&gt; void;
} & Pick&lt;
  UseEmojiPickerType,
  "emojiLibrary" | "focusedCategory" | "i18n" | "icons"
&gt;) {
  return (
    &lt;TooltipProvider delayDuration={500}&gt;
      &lt;nav
        id="emoji-nav"
        className="mb-2.5 border-0 border-b border-solid border-b-border p-1.5"
      &gt;
        <div>
          {emojiLibrary
            .getGrid()
            .sections()
            .map(({ id }) =&gt; (
              &lt;Tooltip key={id}&gt;
                &lt;TooltipTrigger asChild&gt;
                  &lt;Button
                    size="sm"
                    variant="ghost"
                    className={cn(
                      "h-fit rounded-full fill-current p-1.5 text-muted-foreground hover:bg-muted hover:text-muted-foreground",
                      id === focusedCategory &&
                        "pointer-events-none bg-accent fill-current text-accent-foreground",
                    )}
                    onClick={() =&gt; {
                      onClick(id);
                    }}
                    aria-label={i18n.categories[id]}
                    type="button"
                  &gt;
                    <span>
                      {icons.categories[id].outline}
                    </span>
                  &lt;/Button&gt;
                &lt;/TooltipTrigger&gt;
                &lt;TooltipContent side="bottom"&gt;
                  {i18n.categories[id]}
                &lt;/TooltipContent&gt;
              &lt;/Tooltip&gt;
            ))}
        </div>
      &lt;/nav&gt;
    &lt;/TooltipProvider&gt;
  );
}

const emojiCategoryIcons: Record&lt;
  EmojiCategoryList,
  {
    outline: React.ReactElement;
    solid: React.ReactElement; // Needed to add another solid variant - outline will be used for now
  }
&gt; = {
  activity: {
    outline: (
      &lt;svg
        className="size-full"
        fill="none"
        stroke="currentColor"
        strokeLinecap="round"
        strokeLinejoin="round"
        strokeWidth="2"
        viewBox="0 0 24 24"
        xmlns="http://www.w3.org/2000/svg"
      &gt;
        &lt;circle cx="12" cy="12" r="10" /&gt;
        &lt;path d="M2.1 13.4A10.1 10.1 0 0 0 13.4 2.1" /&gt;
        &lt;path d="m5 4.9 14 14.2" /&gt;
        &lt;path d="M21.9 10.6a10.1 10.1 0 0 0-11.3 11.3" /&gt;
      &lt;/svg&gt;
    ),
    solid: (
      &lt;svg
        className="size-full"
        fill="none"
        stroke="currentColor"
        strokeLinecap="round"
        strokeLinejoin="round"
        strokeWidth="2"
        viewBox="0 0 24 24"
        xmlns="http://www.w3.org/2000/svg"
      &gt;
        &lt;circle cx="12" cy="12" r="10" /&gt;
        &lt;path d="M2.1 13.4A10.1 10.1 0 0 0 13.4 2.1" /&gt;
        &lt;path d="m5 4.9 14 14.2" /&gt;
        &lt;path d="M21.9 10.6a10.1 10.1 0 0 0-11.3 11.3" /&gt;
      &lt;/svg&gt;
    ),
  },

  custom: {
    outline: &lt;StarIcon className="size-full" /&gt;,
    solid: &lt;StarIcon className="size-full" /&gt;,
  },

  flags: {
    outline: &lt;FlagIcon className="size-full" /&gt;,
    solid: &lt;FlagIcon className="size-full" /&gt;,
  },

  foods: {
    outline: &lt;AppleIcon className="size-full" /&gt;,
    solid: &lt;AppleIcon className="size-full" /&gt;,
  },

  frequent: {
    outline: &lt;ClockIcon className="size-full" /&gt;,
    solid: &lt;ClockIcon className="size-full" /&gt;,
  },

  nature: {
    outline: &lt;LeafIcon className="size-full" /&gt;,
    solid: &lt;LeafIcon className="size-full" /&gt;,
  },

  objects: {
    outline: &lt;LightbulbIcon className="size-full" /&gt;,
    solid: &lt;LightbulbIcon className="size-full" /&gt;,
  },

  people: {
    outline: &lt;SmileIcon className="size-full" /&gt;,
    solid: &lt;SmileIcon className="size-full" /&gt;,
  },

  places: {
    outline: &lt;CompassIcon className="size-full" /&gt;,
    solid: &lt;CompassIcon className="size-full" /&gt;,
  },

  symbols: {
    outline: &lt;MusicIcon className="size-full" /&gt;,
    solid: &lt;MusicIcon className="size-full" /&gt;,
  },
};

const emojiSearchIcons = {
  delete: &lt;XIcon className="size-4 text-current" /&gt;,
  loupe: &lt;SearchIcon className="size-4 text-current" /&gt;,
};
